package org.bob.web.controller;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionBoostMode;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionScore;
import co.elastic.clients.elasticsearch._types.query_dsl.FunctionScoreQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.MatchAllQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.RandomScoreFunction;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import lombok.extern.slf4j.Slf4j;
import org.bob.web.model.SearchReq;
import org.bob.web.model.SearchRes;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 搜索内容控制类
 */
@Slf4j
@CrossOrigin
@RestController
public class SearchController extends BaseController{

    @Autowired
    private ElasticsearchClient elasticsearchClient;


    @GetMapping("/matchall")
    public Mono<SearchRes> matchall(@RequestBody SearchReq req){
        SearchRequest searchRequest = SearchRequest.of(s->s.index(req.getIndex()).source(so->so.filter(f->f.includes("title"))).query(q->q.matchAll(MatchAllQuery.of(m->m.boost(2.0F)))));
        SearchResponse<Object> search;
        try {
            search = elasticsearchClient.search(searchRequest, Object.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return toMono(search);
    }


    @GetMapping("/searchByTerm")
    public Mono<SearchRes> searchByTerm(@RequestBody SearchReq req){
        SearchRequest searchRequest = SearchRequest.of(s->s.index(req.getIndex()).query(q->q.term(t->t.field("city").value("北京"))));
        SearchResponse<Object> search;
        try {
            search = elasticsearchClient.search(searchRequest, Object.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return toMono(search);
    }

    @GetMapping("/searchByAnd")
    public Mono<SearchRes> searchByBool(){
        SearchRequest searchRequest = SearchRequest.of(s->s.index("hotel").query(q->q.bool(mt->mt.must(tq1->tq1.term(tm1->tm1.field("city").value("北京"))).must(tq2->tq2.term(tm2->tm2.field("price").value(556))))));
        SearchResponse<Object> search;
        try {
            search = elasticsearchClient.search(searchRequest, Object.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return toMono(search);
    }

    @GetMapping("/searchByOr")
    public Mono<SearchRes> searchByOr(){
        SearchRequest searchRequest = SearchRequest.of(s->s.index("hotel").query(q->q.bool(builder->builder.should(tq1->tq1.term(tm1->tm1.field("city").value("北京"))).should(tq2->tq2.term(tm2->tm2.field("city").value("济南"))))));
        SearchResponse<Object> search;
        try {
            search = elasticsearchClient.search(searchRequest, Object.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return toMono(search);
    }


    @GetMapping("/searchByNot")
    public Mono<SearchRes> searchByNot(){
        SearchRequest searchRequest = SearchRequest.of(s->s.index("hotel").query(q->q.bool(builder->builder.mustNot(tq1->tq1.term(tm1->tm1.field("city").value("北京"))).mustNot(tq2->tq2.term(tm2->tm2.field("price").value(182.23d))))));
        SearchResponse<Object> search;
        try {
            search = elasticsearchClient.search(searchRequest, Object.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return toMono(search);
    }


    @GetMapping("/searchByFilter")
    public Mono<SearchRes> searchByFilter(){
        SearchRequest searchRequest = SearchRequest.of(s->s.index("hotel").query(q->q.bool(builder->builder.filter(tq1->tq1.term(tm1->tm1.field("city").value("济南"))).filter(tq2->tq2.term(tm2->tm2.field("price").value(212.23d))))));
        SearchResponse<Object> search;
        try {
            search = elasticsearchClient.search(searchRequest, Object.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return toMono(search);
    }


    @GetMapping("/searchByFunctionSocre")
    public Mono<SearchRes> searchByFunctionSocre(){
        FunctionScoreQuery dd;
        SearchRequest searchRequest = SearchRequest.of(s->s.index("hotel").query(q->q.functionScore(builder->builder.functions(FunctionScore.of(qq->qq.randomScore(new RandomScoreFunction.Builder().build()))).query(aa->aa.term(tt->tt.field("city").value("济南"))).boostMode(FunctionBoostMode.Sum))));
        SearchResponse<Object> search;
        try {
            search = elasticsearchClient.search(searchRequest, Object.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return toMono(search);
    }

    /**
     * 搜索内容方法
     * 1.问题一  默认分页大小是10吗？
     * @return
     */
    @GetMapping("/search")
    public Mono<SearchRes> search(@RequestBody SearchReq req){
        SearchRequest searchRequest;
        /*if(req.getPageSize()==0){
             searchRequest = SearchRequest.of(s -> s
                    .index(req.getIndex())
                    .query(q -> q.match(builder -> builder.field("title").query("酒店"))
                    ));
        }
        else {
            searchRequest = SearchRequest.of(s -> s
                    .index(req.getIndex())
                    .query(q -> q.match(builder -> builder.field("title").query("酒店"))
                    ).from((req.getPageIndex()-1)* req.getPageSize()).size(req.getPageSize()));
        }*/
        List<FieldValue> dd = new ArrayList<>();
        dd.add(FieldValue.of("济南"));
        dd.add(FieldValue.of("北京"));
        searchRequest = SearchRequest.of(s -> s
                .index(req.getIndex())
                .query(q -> q.terms(builder -> builder.field("city").terms(qry-> qry.value(dd)))
                ).from((req.getPageIndex()-1)* req.getPageSize()).size(req.getPageSize()));

        SearchResponse<Object> search;
        try {
            search = elasticsearchClient.search(searchRequest, Object.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
       /* log.info("查询结果:{}", search.hits().hits().get(0));*/
        Mono<List<Object>> applogs =  Mono.just(search.hits().hits().stream().map(hit->hit.source()).collect(Collectors.toList()));
        Mono<Long> total = Mono.just(search.hits().total().value());

        SearchRes searchRes = new SearchRes();
        searchRes.setData(search.hits().hits().stream().map(hit->hit.source()).collect(Collectors.toList()));
        searchRes.setSize(searchRes.getData().size());

        return Mono.just(searchRes);

    }
}
